package com.pyip.simpleioc.load.xml;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import com.pyip.simpleioc.beans.definition.BeanDefinition;
import com.pyip.simpleioc.beans.definition.BeanDefinitionImpl;
import com.pyip.simpleioc.beans.definition.PropertyValues;

public class XmlParser {
    private static final Logger LOGGER = LoggerFactory.getLogger(XmlParser.class);

    private static final String SCHEMA_LOCATION;

    static {
        SCHEMA_LOCATION = ClassLoader.getSystemResource("xsd/ioc-beans-0.1.xsd").getPath();
    }

    public Map<String, BeanDefinition> parseXML(String path) {
        String filename = path.substring(path.lastIndexOf("/"));
        try {
            validate(path);
        } catch (SAXParseException e) {
            throw new XmlBeanDefinitionException("Line " + e.getLineNumber() + " in XML document [" + filename + "] is invalid, " + e.getMessage(), e);
        } catch (SAXException e) {
            throw new XmlBeanDefinitionException("XML document [" + filename + "] is invalid, " + e.getMessage(), e);
        } catch (IOException e) {
            throw new XmlBeanDefinitionException("XML document [" + filename + "] is invalid, " + e.getMessage(), e);
        }

        Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();

        File xmlFile = new File(path);
        SAXReader reader = new SAXReader();
        try {
            Document doc = reader.read(xmlFile);
            Element root = doc.getRootElement();
            for (Iterator<?> topElements = root.elementIterator(); topElements.hasNext();) {
                Element topElement = (Element) topElements.next();
                if (topElement.getName().equals("bean")) {
                    BeanDefinition beanDefinition = new BeanDefinitionImpl();
                    // set attribute
                    // QName qNameId = QName.get("id", root.getNamespaceForURI("http://www.pyip.com/ioc-beans"));
                    // QName qNameId = QName.get("id", root.getNamespace());
                    // String beanId = topElement.attributeValue(qNameId);
                    String beanId = topElement.attributeValue("id");
                    String beanClass = topElement.attributeValue("class");
                    beanDefinition.setId(beanId);
                    beanDefinition.setBeanClass(beanClass);
                    LOGGER.debug("{}[id:{}, class:{}]", topElement.getName(), beanId, beanClass);

                    // set property
                    PropertyValues propertyValues = new PropertyValues();
                    for (Iterator<?> properties = topElement.elementIterator(); properties.hasNext();) {
                        Element property = (Element) properties.next();
                        String name = property.attribute("name").getValue();
                        String value = property.attribute("value").getValue();
                        propertyValues.add(name, value);
                        LOGGER.debug("{}[name:{}, value:{}]", property.getName(), name, value);
                    }
                    beanDefinition.setPropertyValues(propertyValues);
                    // add attribute to map
                    beanDefinitionMap.put(beanId, beanDefinition);
                }
            }
        } catch (DocumentException e) {
            throw new XmlBeanDefinitionException("parse XML document [" + filename + "] failed, " + e.getMessage(), e);
        }

        return beanDefinitionMap;
    }

    private void validate(String path) throws SAXException, IOException {
        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        File file = new File(SCHEMA_LOCATION);
        Schema schema = factory.newSchema(file);

        Validator validator = schema.newValidator();
        validator.validate(new StreamSource(path));
    }
}
